/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package project2analisisnumerico.procesos;

import org.nfunk.jep.JEP;
import org.lsmp.djep.djep.DJep; //objeto especial para derivar  
import org.nfunk.jep.Node;
import org.nfunk.jep.ParseException;
import project2analisisnumerico.main.interfaceProject;
/**
 *
 * @author vmelendez
 */
public class MetodosNumericos {
    
    private JEP myParser;
    private double M;
    
    // variables para el polinomio de Fourier
    
    //Coeficientes de Fourier
     private int MAX_ARMONICOS=0;
     private double[] a ;
     private double[] b ;
    //Discontinuidades
     private int discontN;
     private int discontI;       //discontinuidad actual
     private double P;           //periodo
    //Variables de control
     private int armonicoI=0;         //armónico
     private int Q=1;                 //seno o coseno
    //Número de divisones en cada intervalo
     private int[] divisionesN;
     private double[] X;
    
    public MetodosNumericos(){
        myParser = new JEP();
        myParser = new JEP();
        myParser.initFunTab(); // clear the contents of the function table
        myParser.addStandardFunctions();
        myParser.setTraverse(false);
        myParser.initSymTab(); // clear the contents of the symbol table
	myParser.addStandardConstants();
	myParser.addComplex(); // among other things adds i to the symbol table
    }
    
    /**
     *
     * metodos que calculan el polinomio de Fourier con el metodo Simpsom
     */    
    
    public String[] fourier(double[] tiempo, int nDiscont){
        this.discontN = nDiscont;
        this.MAX_ARMONICOS = nDiscont;
        a = new double[MAX_ARMONICOS];
        b = new double[MAX_ARMONICOS];
        //periodo
         P = tiempo[nDiscont-1]-tiempo[0];
        //transformación de variable, de perido P a perido 2PI
         X = new double[nDiscont];
         X[0] = -Math.PI;
         X[nDiscont-1] = Math.PI;
         for(int i=1; i<nDiscont-1; i++){
              X[i]=(tiempo[i]-tiempo[0])*2*Math.PI/(tiempo[nDiscont-1]-tiempo[0])-Math.PI;
         }

         for(int i=0; i<MAX_ARMONICOS; i++){
              a[i]=b[i]=0.0;
         }
         divisionesN = new int[nDiscont-1];
        return this.retornarCoeficientes();
    }
    
    public double simpson(double a, double b, int n, int discontI){
        try{
            //if(this.validarFuncion(a, funcion, variable) == 1){
                this.M = 20;
                double N = this.M/2;
                double h = (b-a)/(2*N);
                double suma = fS(a, discontI) + fS(b, discontI);

                for(int j=1; j<=N; j++){
                    suma = suma + 4*fS((a+(2*j-1)*h), discontI);
                }

                for(int j=1; j<=N; j++){
                    suma = suma + 2*fS(a+(2*j*h), discontI);
                }

                return redondear((suma*h/3),3) ;
            //}
        }catch(Exception e){
                
        }
        return 0.0;
    }
    
      private double fS(double x, int discontI){
          
          if(discontI == 0){
              return -1*(((x*P)/(2*Math.PI))*Math.sin(armonicoI*x+Q*Math.PI/2));
          }else{
              return ((x*P)/(2*Math.PI))*Math.sin(armonicoI*x+Q*Math.PI/2);
          }
      }
    
    public void calcular(){
        //se asignana las divisiones para el intervalo 0 - 2PI
         for(int i=0; i<discontN-1; i++){
              divisionesN[i] = (int)((X[i+1]-X[i])*50/Math.PI);
         }
        //calcula los coeficientes a
         Q=1;      //coseno
         for(armonicoI=0; armonicoI<MAX_ARMONICOS; armonicoI++){
              for(discontI=0; discontI<discontN-1; discontI++){
                   a[armonicoI]+=simpson(X[discontI], X[discontI+1], divisionesN[discontI],discontI)/Math.PI;
              }
         }
        //calcula los coeficientes b
         Q=0;	//seno
         for(armonicoI=0; armonicoI<MAX_ARMONICOS; armonicoI++){
              for(discontI=0; discontI<discontN-1; discontI++){
                   b[armonicoI]+=simpson(X[discontI], X[discontI+1], divisionesN[discontI],discontI)/Math.PI;
              }
         }
    }
    
     public String[] retornarCoeficientes(){
        calcular();
        String coef = "";
        String funcion = "";
        ///System.out.println("a[0] "+redondear(a[0],3));
        coef = coef + "a[0] " + redondear(a[0],3) + "\n";
        funcion = funcion + redondear(a[0],3);
        for(int i=1; i<MAX_ARMONICOS; i++){
            coef = coef + "a[" + i + "] = "+Math.floor(a[i]*1000)/1000+"\t" + " b["+i+"] = " + Math.floor(b[i]*1000)/1000 + "\n";
        }
        for(int i=1; i<MAX_ARMONICOS; i++){
            funcion = funcion + (a[i]<0 ? redondear(a[i],3): "+" + redondear(a[i],3)) + "*cos(" + i + "*X)" + (b[i]<0 ? redondear(b[i],3): "+" + redondear(b[i],3)) + "*sin(" + i + "*X)";
        }
        System.out.println(funcion);
        return new String[] {coef,funcion};
   }
    
     /**
     *
     * metodo que calcula el area, longitud y volumen de una funcion con el metodo trapezoidal
     */  
    public double[] trapezoidal(double a, double b, int n, String funcion, String variable){
        try{
            if(this.validarFuncion(a, funcion, variable) == 1){
                
                return new double[] { this.obtenerArea(a,b,n,funcion,variable), this.obtenerVolumen(a,b,n,funcion,variable), this.obtenerLongitud(a, b, n, funcion, variable)};
            }
        }catch(Exception e){
            interfaceProject.konsole.imprimirConsola("No se pudo ejecutar la funcion en: Integracion Trapezoidal");
            interfaceProject.konsole.imprimirConsola("Error:");
            //interfaceProject.konsole.imprimirConsola(e.getMessage());
        }
        return null;
    }
    
    private double obtenerArea(double a, double b, int n, String funcion, String variable){
        double h=(b-a)/20;
        double suma = (f(a,funcion,variable) + f(b,funcion,variable))/2;
        for(int i =1 ; i<n; i++){
            suma = suma + f(a+i*h,funcion,variable);
        }
        return this.redondear(suma*h,3);
    }
    
    private double obtenerVolumen(double a, double b, int n, String funcion, String variable){
        double h=(b-a)/20;
        String nf = "(" + funcion + ")^2";
        double vol = (f(a,nf,variable) + f(b,nf,variable))/2;
        for(int i = 1; i<n; i++){
            vol = vol + f(a+i*h,nf,variable);
        }
        vol = vol*h;
        vol = vol*Math.PI;
        return this.redondear(vol,3);
    }
    
    private double obtenerLongitud(double a, double b, int n, String funcion, String variable){
        double h=(b-a)/20;
        String derivada = this.derivar(funcion, variable);
        String gx = "sqrt(1 + (" + derivada + ")^2)";
        double longitud = (f(a,gx,variable) + f(b,gx,variable))/2;
        for(int i=1; i<n; i++){
            longitud = longitud + f(a+i*h,gx,variable);
        }
        return this.redondear(longitud*h,3);
    }
    
    //Metodos de apoyo
  
    public double f(double x, String Funcion, String variable){
	myParser.addVariable(variable, x);
	myParser.parseExpression(Funcion);
        return Double.parseDouble(myParser.getValueAsObject().toString());

    }
    
    public int validarFuncion(double x, String Funcion, String variable){
        myParser.addVariable(variable, x);
	myParser.parseExpression(Funcion);
        if(myParser.getErrorInfo() == null){
            //System.out.println(myParser.getValueAsObject().toString());
            return 1;
        }else{
            interfaceProject.konsole.imprimirConsola("No se pudo evaluar la funcion en: Integracion Trapezoidal");
            interfaceProject.konsole.imprimirConsola("Error: no se ha escrito bien la funcion, ejemplo: 3+2*4/2");
            return 0;
        }
    }
    
    public double redondear(double numero,int digitos){
      int cifras=(int) Math.pow(10,digitos);
      return Math.rint(numero*cifras)/cifras;
    }
    
    public String derivar(String Funcion, String variable){  
  
        String derivada = "";  
        DJep djep = new DJep();  
        djep.addStandardConstants();  
        djep.addStandardFunctions();  
        djep.addComplex();  
        djep.setAllowUndeclared(true);  //permitir variables no declaradas  
        djep.setAllowAssignment(true);  //permite asignaciones  
        djep.setImplicitMul(true);      //regla de multiplicacion o para sustraccion y sumas  
        djep.addStandardDiffRules();  

        try{  
            Node node = djep.parse(Funcion);  
            Node diff = djep.differentiate(node,variable); //derivamos con respecto a x  
            Node simp = djep.simplify(diff);  //simplificamos el resultado  
            derivada =  djep.toString(simp); //convertimos a String  
        }  

        catch(ParseException e){  
            e.printStackTrace(); 
            interfaceProject.konsole.imprimirConsola("Error: no se ha podido derivar la funcion: "+Funcion);
        }
        interfaceProject.konsole.imprimirConsola(derivada);
        return derivada; //regresamos la funcion derivada  
    }  

}

/*
 * 
sin(variable) para Seno
cos(variable) para Coseno
tan(variable) para Tangente
asin(variable) para ArcoSeno
acos(variable) para ArcoCoseno
atan(variable) para ArcoTangente
sinh(variable) para Seno Hiperbolico
cosh(variable) para Coseno Hiperbolico
tanh(variable) para Tangente Hiperbolica
ln(variable) para Logaritmo Natura
log(variable) para Logaritmo en base 10
lg(variable) para Logaritmo en base 2
exp(variable) para exponencial
 */